﻿using System;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Input;
using Monti.Docking;
using Monti.MVVM;
using WikiUploader.Classes.MediaWikiAccess;
using WikiUploader.Controls;

namespace WikiUploader.Classes
{
   public class ExcerptUploaderModel : DockItem
   {

      #region Fields

      private RelayCommand m_CmdUploadToWiki;
      private RelayCommand m_CmdClearPassage;
      private RelayCommand m_CmdEditVerse;

      private string m_ChapterPageTitle = string.Empty;
      private string m_ChapterContent = string.Empty;
      private string m_ExcerptPageTitle = string.Empty;
      private string m_ExcerptTemplateBook = string.Empty;
      private string m_ExcerptTemplateChapter = string.Empty;
      private string m_ExcerptTemplateVerse = string.Empty;
      private string m_ExcerptTemplateHeader = string.Empty;
      private string m_ExcerptTemplateFooter = string.Empty;
      private string m_ExcerptTemplatePassage = string.Empty;
      private string m_ExcerptContent = string.Empty;
      private string m_ChapterTemplateBook = string.Empty;
      private string m_ChapterTemplateChapter = string.Empty;
      private string m_ChapterTemplateLimit = string.Empty;
      private string m_ChapterTemplatePrepend = string.Empty;
      private string m_ChapterTemplateAppend = string.Empty;
      private string m_LastVerse = string.Empty;

      private readonly ExcerptUploaderView m_View;
      private readonly MediaWikiSite m_Site;
      private bool m_ReplaceLineBreaks;

      #endregion

      #region Constructor 
      
      /// <summary>
      /// Initializes a new excerpt uploader object.
      /// </summary>
      /// <param name="site">The wiki site object.</param>
      public ExcerptUploaderModel (MediaWikiSite site)
      {
         this.m_Site = site;
         this.Title = "Excerpt Uploader";
         this.m_View = new ExcerptUploaderView {DataContext = this};
         this.Content = this.m_View;
      }

      #endregion

      #region Excerpt

      /// <summary>
      /// Gets or Sets a value indicating whether or not to replace line breaks.
      /// </summary>
      public bool ReplaceLineBreaks
      {
         get { return this.m_ReplaceLineBreaks; }
         set
         {
            this.m_ReplaceLineBreaks = value;
            this.RaisePropertyChanged ("ReplaceLineBreaks");
         }
      }

      /// <summary>
      /// Gets or Sets the Excerpt Wiki Page title.
      /// </summary>
      public string ExcerptPageTitle
      {
         get { return this.m_ExcerptPageTitle; }
         set
         {
            this.m_ExcerptPageTitle = value;
            this.RaisePropertyChanged ("ExcerptPageTitle");
         }
      }

      /// <summary>
      /// Gets or Sets the Excerpt's book title.
      /// </summary>
      public string ExcerptTemplateBook
      {
         get { return this.m_ExcerptTemplateBook; }
         set
         {
            this.m_ExcerptTemplateBook = value;
            this.RaisePropertyChanged ("ExcerptTemplateBook");
            this.SetExcerptTitle ();
            this.BuildExcerptContent ();
         }
      }

      /// <summary>
      /// Gets or Sets the Excerpt's chapter.
      /// </summary>
      public string ExcerptTemplateChapter
      {
         get { return this.m_ExcerptTemplateChapter; }
         set
         {
            this.m_ExcerptTemplateChapter = value;
            this.RaisePropertyChanged ("ExcerptTemplateChapter");
            this.SetExcerptTitle ();
            this.BuildExcerptContent ();
         }
      }

      /// <summary>
      /// Gets or Sets the Excerpt's verse.
      /// </summary>
      public string ExcerptTemplateVerse
      {
         get { return this.m_ExcerptTemplateVerse; }
         set
         {
            this.m_ExcerptTemplateVerse = value;
            this.RaisePropertyChanged ("ExcerptTemplateVerse");
            this.SetExcerptTitle ();
            this.BuildExcerptContent ();
         }
      }

      private string ReplaceBreaks (string verse)
      {
         return verse.Replace ("\r\n", "<br />").Replace ("\r", "<br />").Replace ("\n", "<br />");
      }

      /// <summary>
      /// Gets or Sets the Excerpt's header.
      /// </summary>
      public string ExcerptTemplateHeader
      {
         get { return this.m_ExcerptTemplateHeader; }
         set
         {
            this.m_ExcerptTemplateHeader = value;
            this.RaisePropertyChanged ("ExcerptTemplateHeader");
            this.BuildExcerptContent ();
         }
      }

      /// <summary>
      /// Gets or Sets the Excerpt's footer.
      /// </summary>
      public string ExcerptTemplateFooter
      {
         get { return this.m_ExcerptTemplateFooter; }
         set
         {
            this.m_ExcerptTemplateFooter = value;
            this.RaisePropertyChanged ("ExcerptTemplateFooter");
            this.BuildExcerptContent ();
         }
      }

      /// <summary>
      /// Gets or Sets the Excerpt's passage.
      /// </summary>
      public string ExcerptTemplatePassage
      {
         get { return this.m_ExcerptTemplatePassage; }
         set
         {
            var passage = value;
            if (this.ReplaceLineBreaks)
               passage = this.ReplaceBreaks (passage);
            if (passage.Contains ("*") && string.IsNullOrEmpty (this.ExcerptTemplateFooter))
               this.ExcerptTemplateFooter = "<small>*</small>";
            this.m_ExcerptTemplatePassage = passage;
            this.RaisePropertyChanged ("ExcerptTemplatePassage");
            this.BuildExcerptContent ();
         }
      }

      /// <summary>
      /// Gets or Sets the Excerpt's content.
      /// </summary>
      public string ExcerptContent
      {
         get { return this.m_ExcerptContent; }
         set
         {
            this.m_ExcerptContent = value;
            this.RaisePropertyChanged ("ExcerptContent");
         }
      }

      #endregion

      #region Chapter

      /// <summary>
      /// Gets or Sets the Chapter Wiki Page title.
      /// </summary>
      public string ChapterPageTitle
      {
         get { return this.m_ChapterPageTitle; }
         set
         {
            this.m_ChapterPageTitle = value;
            this.RaisePropertyChanged ("ChapterPageTitle");
         }
      }

      /// <summary>
      /// Gets or Sets the Chapter's content.
      /// </summary>
      public string ChapterContent
      {
         get { return this.m_ChapterContent; }
         set
         {
            this.m_ChapterContent = value;
            this.RaisePropertyChanged ("ChapterContent");
         }
      }

      /// <summary>
      /// Gets or Sets the Chapter's book title.
      /// </summary>
      public string ChapterTemplateBook
      {
         get { return this.m_ChapterTemplateBook; }
         set
         {
            this.m_ChapterTemplateBook = value;
            this.RaisePropertyChanged ("ChapterTemplateBook");
            this.SetChapterTitle ();
            this.BuildChapterContent ();
         }
      }

      /// <summary>
      /// Gets or Sets the Chapter's chapter.
      /// </summary>
      public string ChapterTemplateChapter
      {
         get { return this.m_ChapterTemplateChapter; }
         set
         {
            this.m_ChapterTemplateChapter = value;
            this.RaisePropertyChanged ("ChapterTemplateChapter");
            this.SetChapterTitle ();
            this.BuildChapterContent ();
         }
      }

      /// <summary>
      /// Gets or Sets the Chapter's limit.
      /// </summary>
      public string ChapterTemplateLimit
      {
         get { return this.m_ChapterTemplateLimit; }
         set
         {
            this.m_ChapterTemplateLimit = value;
            this.RaisePropertyChanged ("ChapterTemplateLimit");
            this.BuildChapterContent ();
         }
      }

      /// <summary>
      /// Gets or Sets the data to prepend to the chapter content.
      /// </summary>
      public string ChapterTemplatePrepend
      {
         get { return this.m_ChapterTemplatePrepend; }
         set
         {
            this.m_ChapterTemplatePrepend = value;
            this.RaisePropertyChanged ("ChapterTemplatePrepend");
            this.BuildChapterContent ();
         }
      }

      /// <summary>
      /// Gets or Sets the data to append to the chapter content.
      /// </summary>
      public string ChapterTemplateAppend
      {
         get { return this.m_ChapterTemplateAppend; }
         set
         {
            this.m_ChapterTemplateAppend = value;
            this.RaisePropertyChanged ("ChapterTemplateAppend");
            this.BuildChapterContent ();
         }
      }

      #endregion

      /// <summary>
      /// Gets or Sets the command to upload to the wiki.
      /// </summary>
      public ICommand UploadToWikiCommand
      {
         get { return this.m_CmdUploadToWiki ?? (this.m_CmdUploadToWiki = new RelayCommand (this.Upload, this.CanUpload)); }
      }

      /// <summary>
      /// Gets or Sets the command to clear the passage.
      /// </summary>
      public ICommand ClearPassageCommand
      {
         get { return this.m_CmdClearPassage ?? (this.m_CmdClearPassage = new RelayCommand (this.ClearPassage)); }
      }

      /// <summary>
      /// Gets or Sets the command to edit the verse.
      /// </summary>
      public ICommand EditVerseCommand
      {
         get { return this.m_CmdEditVerse ?? (this.m_CmdEditVerse = new RelayCommand (this.EditVerse, this.CanEditVerse)); }
      }

      /// <summary>
      /// Check to see if the edit verse command can execute.
      /// </summary>
      /// <param name="obj">Command parameter.</param>
      /// <returns>True if command can execute, otherwise false.</returns>
      private bool CanEditVerse (object obj)
      {
         return !string.IsNullOrEmpty (this.ExcerptTemplateVerse);
      }

      /// <summary>
      /// Edits the verse.
      /// </summary>
      /// <param name="obj">Command parameter.</param>
      private void EditVerse (object obj)
      {
         var verse = Convert.ToInt32 (this.ExcerptTemplateVerse);
         if (obj.ToString () == "-")
            this.ExcerptTemplateVerse = Convert.ToString (verse - 1);
         else if (obj.ToString () == "+")
            this.ExcerptTemplateVerse = Convert.ToString (verse + 1);
      }

      /// <summary>
      /// Clears the passage text.
      /// </summary>
      /// <param name="obj">Command parameter.</param>
      private void ClearPassage (object obj)
      {
         this.ExcerptTemplatePassage = string.Empty;
         this.ExcerptTemplateHeader = string.Empty;
         this.ExcerptTemplateFooter = string.Empty;
      }

      /// <summary>
      /// Checks to see if the upload is possible.
      /// </summary>
      /// <param name="obj">String value of what to upload ('Excerpt' or 'Chapter').</param>
      /// <returns>Returns true if the command is ok to execute, otherwise false.</returns>
      private bool CanUpload (object obj)
      {
         var whatToUpload = obj.ToString ();
         if (whatToUpload == "Excerpt")
         {
            return !string.IsNullOrEmpty (this.ExcerptTemplateBook) &&
                   !string.IsNullOrEmpty (this.ExcerptTemplateVerse) &&
                   !string.IsNullOrEmpty (this.ExcerptTemplatePassage);
         }
         if (whatToUpload == "Chapter")
         {
            return !string.IsNullOrEmpty (this.ChapterTemplateBook) &&
                   !string.IsNullOrEmpty (this.ChapterTemplateLimit);
         }

         return false;
      }

      /// <summary>
      /// Uploads the data to the wiki.
      /// </summary>
      /// <param name="obj">String value of what to upload ('Excerpt' or 'Chapter').</param>
      private void Upload (object obj)
      {
         if (obj.ToString () == "Excerpt")
         {
            if (this.m_LastVerse == this.ExcerptTemplateVerse)
            {
               var messageBox = DialogService.Instance.GetService <MessageBoxDialogService> ();
               messageBox.DialogCaption = "Same verse";
               MessageBoxResult result = messageBox.ShowYesNo ("The current and last verse saved are the same, continue?");
               if (result == MessageBoxResult.No)
                  return;
            }

            this.m_LastVerse = this.ExcerptTemplateVerse;
            var page = this.m_Site.GetPage (this.ExcerptPageTitle, false);
            page.Content = this.ExcerptContent;
            page.Save ();
         }
         else if (obj.ToString () == "Chapter")
         {
            var page = this.m_Site.GetPage (this.ChapterPageTitle, false);
            page.Content = this.ChapterContent;
            page.Save ();
         }
      }

      /// <summary>
      /// Sets the Excerpt title.
      /// </summary>
      private void SetExcerptTitle ()
      {
         this.ExcerptPageTitle = string.IsNullOrEmpty (this.ExcerptTemplateChapter) ?
            string.Format ("{0}:{1}", this.ExcerptTemplateBook, this.ExcerptTemplateVerse) :
            string.Format ("{0}:{1}:{2}", this.ExcerptTemplateBook,
               this.ExcerptTemplateChapter, this.ExcerptTemplateVerse);
      }

      /// <summary>
      /// Sets the Chapter title.
      /// </summary>
      private void SetChapterTitle ()
      {
         this.ChapterPageTitle = string.IsNullOrEmpty (this.ChapterTemplateChapter) ?
            string.Format ("{0}", this.ChapterTemplateBook) :
            string.Format ("{0}:{1}", this.ChapterTemplateBook, this.ChapterTemplateChapter);
      }

      private void BuildExcerptContent ()
      {
         var content = new StringBuilder ();
         content.AppendLine ("{{Excerpt");
         content.AppendLine (string.Format ("| book = {0}", this.ExcerptTemplateBook));
         if (!string.IsNullOrEmpty (this.ExcerptTemplateChapter))
            content.AppendLine (string.Format ("| chapter = {0}", this.ExcerptTemplateChapter));
         content.AppendLine (string.Format ("| verse = {0}", this.ExcerptTemplateVerse));
         if (this.ExcerptTemplatePassage.Contains ("[[") || this.ExcerptTemplatePassage.Contains ("]]") ||
             this.ExcerptTemplatePassage.Contains ("{{") || this.ExcerptTemplatePassage.Contains ("}}"))
            content.AppendLine (string.Format ("| temppassage = {0}", this.ExcerptTemplatePassage));
         else
            content.AppendLine (string.Format ("| passage = {0}", this.ExcerptTemplatePassage));
         if (!string.IsNullOrEmpty (this.ExcerptTemplateHeader) && !this.ExcerptTemplateHeader.Contains ("[["))
            content.AppendLine (string.Format ("| header = {0}", this.ExcerptTemplateHeader));
         if (!string.IsNullOrEmpty (this.ExcerptTemplateFooter) && !this.ExcerptTemplateFooter.Contains ("[["))
            content.AppendLine (string.Format ("| footer = {0}", this.ExcerptTemplateFooter));

         content.AppendLine ("}}");
         if (this.ExcerptTemplatePassage.Contains ("[[") || this.ExcerptTemplatePassage.Contains ("]]") ||
             this.ExcerptTemplatePassage.Contains ("{{") || this.ExcerptTemplatePassage.Contains ("}}"))
         {
            var passage = this.ExcerptTemplatePassage;
            if (passage.Contains ("{{Definition") && passage.Contains ("}}") && passage.Count (c => c == '|') >= 2)
            {
               var index = passage.IndexOf("{{Definition", StringComparison.Ordinal);
               while (index != -1)
               {
                  index = passage.IndexOf ('|', index);
                  if (index != -1)
                     passage = passage.Substring (0, index) + "{{!}}" + passage.Substring (index + 1);
                  index = passage.IndexOf ('|', index);
                  if (index != -1)
                     passage = passage.Substring (0, index) + "{{!}}" + passage.Substring (index + 1);
                  index = passage.IndexOf("{{Definition", index, StringComparison.Ordinal);
               }
            }
            content.AppendLine (string.Format ("{{{{#set:Passage={0}}}}}", passage));
         }
         if (!string.IsNullOrEmpty (this.ExcerptTemplateHeader) && this.ExcerptTemplateHeader.Contains ("[["))
            content.AppendLine (string.Format ("{{{{#set:Header={0}}}}}", this.ExcerptTemplateHeader));
         if (!string.IsNullOrEmpty (this.ExcerptTemplateFooter) && this.ExcerptTemplateFooter.Contains ("[["))
            content.AppendLine (string.Format ("{{{{#set:Footer={0}}}}}", this.ExcerptTemplateFooter));

         this.ExcerptContent = content.ToString ();
      }

      /// <summary>
      /// Builds the chapter content.
      /// </summary>
      private void BuildChapterContent ()
      {
         var content = new StringBuilder ();
         if (!string.IsNullOrEmpty (this.ChapterTemplatePrepend))
            content.AppendLine (this.ChapterTemplatePrepend);

         if (string.IsNullOrEmpty (this.ChapterTemplateChapter))
         {
            content.AppendLine ("{{Article");
            content.AppendLine (string.Format ("| article = {0}", this.ChapterTemplateBook));
            content.AppendLine (string.Format ("| passagelimit = {0}", this.ChapterTemplateLimit));
            content.AppendLine ("}}");
            content.AppendLine ("[[Category:Article]]");
         }
         else
         {
            content.AppendLine ("{{Chapter");
            content.AppendLine (string.Format ("| book = {0}", this.ChapterTemplateBook));
            content.AppendLine (string.Format ("| chapter = {0}", this.ChapterTemplateChapter));
            content.AppendLine (string.Format ("| passagelimit = {0}", this.ChapterTemplateLimit));
            content.AppendLine ("}}");
         }

         if (!string.IsNullOrEmpty (this.ChapterTemplateAppend))
            content.AppendLine (this.ChapterTemplateAppend);

         this.ChapterContent = content.ToString ();
      }
   }
}
